﻿using Define;
using LogicCenter.Logic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Tools.Base;
using Tools.EventObjectMoudle;

namespace LogicCenter.AssemblyPatch
{
    public class AssemblyPatchManager : SingletonTemplate<AssemblyPatchManager>, IEventListener
    {
        public bool Init()
        {
            if(!InitConfig())
            {
                return false;
            }

            // 向事件源反注册监听器
            GlobalEventSource.getInstance().removeListener((int)EventTypes.PlayerInit, AssemblyPatchManager.Instance());
            GlobalEventSource.getInstance().removeListener((int)EventTypes.PlayerOnline, AssemblyPatchManager.Instance());
            GlobalEventSource.getInstance().removeListener((int)EventTypes.GameRunning, AssemblyPatchManager.Instance());

            // 向事件源注册监听器
            List<EventTypes> list = patchCfgDict.Keys.ToList();
            foreach(var item in list)
            {
                GlobalEventSource.getInstance().registerListener((int)item, AssemblyPatchManager.Instance());
            }

            return true;
        }

        /// <summary>
        /// 程序里要加载的配置列表 通过静态表配置所得
        /// 不在该列表里的，即使已经加载了相关内容，也不会执行
        /// </summary>
        private Dictionary<EventTypes, List<MethodConfig>> patchCfgDict = new Dictionary<EventTypes, List<MethodConfig>>();

        private bool[] CmdRegisteredFlags = new bool[32768];

        /// <summary>
        /// 加载配置表
        /// </summary>
        /// <returns></returns>
        public bool InitConfig()
        {
            string fileName = "AssemblyPatch.xml";
            try
            {
                if (!File.Exists(fileName))
                    return false;

                XElement xml = XElement.Load(fileName);
                if (null == xml)
                    return false;

                Dictionary<EventTypes, List<MethodConfig>> tmpPatchCfgDict = new Dictionary<EventTypes, List<MethodConfig>>();
                bool[] flags = new bool[32768];

                // 版本比较，此处逻辑略过

                IEnumerable<XElement> xmlItems = xml.Elements();
                foreach (var xmlItem in xmlItems)
                {
                    MethodConfig config = new MethodConfig();
                    Enum.TryParse(XmlTool.GetDefAttributeStr(xmlItem, "Type", ""), true, out config.eventType);
                    config.assembleName = XmlTool.GetDefAttributeStr(xmlItem, "AssemblyName", "");
                    config.fullClassName = XmlTool.GetDefAttributeStr(xmlItem, "fullClassName", "");
                    config.methodName = XmlTool.GetDefAttributeStr(xmlItem, "methodName", "");
                    config.methodParams = XmlTool.GetDefAttributeStr(xmlItem, "methodParams", "").Split(',');
                    config.cmdID = (int)XmlTool.GetSafeAttributeLong(xmlItem, "CmdID");

                    // 对列表里的配置进行检查
                    if (!CheckMethod(config))
                    {
                        continue;
                    }

                    flags[config.cmdID] = true;
                    if (tmpPatchCfgDict.ContainsKey(config.eventType))
                    {
                        tmpPatchCfgDict[config.eventType].Add(config);
                    }
                    else
                    {
                        List<MethodConfig> methodList = new List<MethodConfig>();
                        methodList.Add(config);
                        tmpPatchCfgDict[config.eventType] = methodList;
                    }
                }
                patchCfgDict = tmpPatchCfgDict;
                CmdRegisteredFlags = flags;
            }
            catch (Exception ex)
            {
                LogManager.WriteLog(string.Format("加载AssemblyPatch文件出错. Msg={0}", ex.Message), ex);
            }

            return true;
        }

        private bool CheckMethod(MethodConfig cfg)
        {
            try
            {
                AssemblyLoader loader = GetAssemblyLoader(cfg.assembleName);
                if(null == loader)
                {
                    loader = new AssemblyLoader();
                    if(!loader.LoadAssembly(cfg.assembleName))
                    {
                        return false;
                    }

                    AddAssemblyLoader(cfg.assembleName, loader);
                }

                return loader.LoadMethod(cfg.fullClassName, cfg.methodName);
            }
            catch(Exception ex)
            {
                LogManager.WriteLog(string.Format("CheckMethod Failed. Msg={0}", ex.Message), ex);
                return false;
            }
        }

        /// <summary>
        /// 程序中已经加载了的动态Dll列表
        /// </summary>
        private Dictionary<string, AssemblyLoader> patchDict = new Dictionary<string, AssemblyLoader>();

        private AssemblyLoader GetAssemblyLoader(string AssemblyName)
        {
            if(!patchDict.ContainsKey(AssemblyName))
            {
                return null;
            }

            return patchDict[AssemblyName];
        }

        private void AddAssemblyLoader(string AssemblyName, AssemblyLoader loader)
        {
            try
            {
                patchDict.Add(AssemblyName, loader);
            }
            catch(Exception ex)
            {
                LogManager.WriteLog(string.Format("AddAssemblyLoader Failed. Msg={0}", ex.Message), ex);
            }
        }

        public void processEvent(EventObject eventObject)
        {
            List<MethodConfig> methodList = null;
            if(!patchCfgDict.TryGetValue((EventTypes)eventObject.getEventType(), out methodList))
            {
                return;
            }
            if(null == methodList || methodList.Count == 0)
            {
                return;
            }

            GameClient client = null;
            switch(eventObject.getEventType())
            {
                case (int)EventTypes.PlayerInit:
                case (int)EventTypes.PlayerOnline:
                    client = eventObject.GetArgs<GameClient>(0);
                    break;
            }

            foreach(var item in methodList)
            {
                AssemblyLoader loader = GetAssemblyLoader(item.assembleName);
                if(null == loader)
                {
                    continue;
                }

                // 执行玩家相关的补丁
                object[] paramarray = null;
                if(null != client)
                {
                    int index = 1;
                    paramarray = new object[item.methodParams.Length + index];
                    paramarray[0] = client;
                    for(int i = 0; i < item.methodParams.Length; i++)
                    {
                        paramarray[i + index] = item.methodParams[i];
                    }
                }
                else
                {
                    paramarray = new object[item.methodParams.Length];
                    for(int i = 0; i < item.methodParams.Length; i++)
                    {
                        paramarray[i] = item.methodParams[i];
                    }
                }
                loader.Invoke(item.fullClassName, item.methodName, paramarray);
            }
        }
    }
}
